/*
 *  levelUpGame - BoundingBox.cpp
 *  Copyright 2011 W. Reckman. All rights reserved.
 */
#include <math.h>
#include "ofGraphics.h"
#include "BoundingBox.h"

//--------------------------------------------------------------
BoundingBox::BoundingBox()
: collisionBBoxScale( 1.0f ), coords( 0, 0 ), prevCoords( 0, 0 ), bBox( 0, 0, 0, 0 )
{}

BoundingBox::BoundingBox( const ofRectangle& bBox_ )
: collisionBBoxScale( 1.0f )
{
	//NOTE: run twice to properly init previous coordinates
	recalcFromBBox( bBox_ );
	recalcFromBBox( bBox_ );
}

BoundingBox::BoundingBox( float x_, float y_, float w_, float h_ )
: collisionBBoxScale( 1.0f )
{
	//NOTE: run twice to properly init previous coordinates
	recalcFromBBox( x_, y_, w_, h_ );
	recalcFromBBox( x_, y_, w_, h_ );
}

BoundingBox::~BoundingBox()
{}


//--------------------------------------------------------------
//NOTE: for debugging purposes only
void BoundingBox::draw() const {
	ofRectangle mySc = bBox; scaleRectangle( mySc, collisionBBoxScale );
	
	ofPushStyle();
	ofSetLineWidth( 2 );
	ofNoFill();
	ofSetColor( 255, 255, 127 );
	ofRect( mySc.x, mySc.y, mySc.width, mySc.height );
	ofPopStyle();
}

//--------------------------------------------------------------
const ofRectangle& BoundingBox::getBBox() const { return bBox; }
const ofPoint& BoundingBox::getPosition() const { return coords; }
float BoundingBox::getCollisionBBoxScale() const { return collisionBBoxScale; }


//--------------------------------------------------------------
void BoundingBox::setCollisionBBoxScale( float scale_ ) { collisionBBoxScale = scale_; }
void BoundingBox::setPosition( const ofPoint& pos_ ) { setPosition( pos_.x, pos_.y ); }
void BoundingBox::setPosition( float x_, float y_ ) { recalcFromPosition( x_, y_ ); }
void BoundingBox::setSize( const ofPoint& dims_ ) { setSize( dims_.x, dims_.y ); }
void BoundingBox::setSize( float w_, float h_ ) { recalcFromDimensions( w_, h_ ); }
void BoundingBox::setBBox( float x_, float y_, float w_, float h_ ) { recalcFromBBox( x_, y_, w_, h_ ); }
void BoundingBox::setBBox( const ofPoint& p_, float w_, float h_ ) { recalcFromBBox( p_.x, p_.y, w_, h_ ); }
void BoundingBox::setCenteredBBox( const ofPoint& p_, float w_, float h_ ) { setCenteredBBox( p_.x, p_.y, w_, h_ ); }

void BoundingBox::setCenteredBBox( float x_, float y_, float w_, float h_ ) {
	recalcFromPosition( x_, y_ );
	recalcFromDimensions( w_, h_ );
}

void BoundingBox::revert() {
//ofLog( OF_LOG_WARNING, "REV (%2.2f, %2.2f) -> (%2.2f, %2.2f)", coords.x, coords.y, prevCoords.x, prevCoords.y ); //TEMP
	coords = prevCoords;
	bBox = prevBBox;
}


//--------------------------------------------------------------
//NOTE: to understand this code, read: http://rbrundritt.wordpress.com/2009/10/03/determining-if-two-bounding-boxes-overlap/
bool BoundingBox::overlapsWith( const BoundingBox& other_ ) const {
	ofRectangle mySc = bBox; scaleRectangle( mySc, collisionBBoxScale );
	ofRectangle otherSc = other_.bBox; scaleRectangle( otherSc, collisionBBoxScale );
	
	ofPoint rAB, sumrArB;
	ofPoint aTL( mySc.x, mySc.y ), aBR( aTL.x + mySc.width, aTL.y + mySc.height );
	ofPoint bTL( otherSc.x, otherSc.y ), bBR( bTL.x + otherSc.width, bTL.y + otherSc.height );
	
	rAB.x = fabs( aTL.x + aBR.x - bTL.x - bBR.x );
	rAB.y = fabs( aTL.y + aBR.y - bTL.y - bBR.y );
	sumrArB.x = aBR.x - aTL.x + bBR.x - bTL.x;
	sumrArB.y = aBR.y - aTL.y + bBR.y - bTL.y;
	
	return rAB.x <= sumrArB.x && rAB.y <= sumrArB.y;
}

//--------------------------------------------------------------
//FIXME: this function has not been tested
//bool BoundingBox::isInside( const BoundingBox& other_ ) const {
//	int x1d = bBox.x - other_.bBox.x;
//	int y1d = bBox.y - other_.bBox.y;
//	int x2d = ( other_.bBox.x + other_.bBox.width ) - ( bBox.x + bBox.width );
//	int y2d = ( other_.bBox.y + other_.bBox.height ) - ( bBox.y + bBox.height );
//	
//	return x1d >= 0 && y1d >= 0 && x2d >= 0 && y2d >= 0;
//}

void BoundingBox::scaleRectangle( ofRectangle& rect_, float scale_ ) {
	float cx = rect_.x + rect_.width / 2.0f, cy = rect_.y + rect_.height / 2.0f;
	float sw = rect_.width * scale_, sh = rect_.height * scale_;
	
	rect_.x = cx - sw / 2.0f;
	rect_.y = cy - sh / 2.0f;
	rect_.width = sw;
	rect_.height = sh;
}


/*********************
 * PRIVATE FUNCTIONS *
 *********************/

void BoundingBox::recalcFromPosition( float x_, float y_ ) {
	prevCoords = coords; prevBBox = bBox;
	coords.x = x_;
	coords.y = y_;
	bBox.x = x_ - bBox.width / 2.0f;
	bBox.y = y_ - bBox.height / 2.0f;
}

void BoundingBox::recalcFromBBox( float x_, float y_, float w_, float h_ ) {
	recalcFromBBox( ofRectangle( x_, y_, w_, h_ ) );
}

void BoundingBox::recalcFromBBox( const ofRectangle& bBox_ ) {
	prevCoords = coords; prevBBox = bBox;
	bBox = bBox_;
	coords.x = bBox_.x + bBox_.width / 2.0f;
	coords.y = bBox_.y + bBox_.height / 2.0f;
}

void BoundingBox::recalcFromDimensions( float w_, float h_ ) {
	prevCoords = coords; prevBBox = bBox;
	bBox.x = coords.x - w_ / 2.0f;
	bBox.y = coords.y - h_ / 2.0f;
	bBox.width = w_;
	bBox.height = h_;
}
